package cn.edu.pku.ss.situationawareperceiver;

import java.util.ArrayList;

import org.alljoyn.bus.BusException;
import org.alljoyn.bus.BusObject;
import org.alljoyn.bus.Status;
import org.alljoyn.bus.annotation.BusSignalHandler;
import org.alljoyn.cops.peergroupmanager.BusObjectData;
import org.alljoyn.cops.peergroupmanager.JoinOrCreateReturn;
import org.alljoyn.cops.peergroupmanager.PeerGroupListener;
import org.alljoyn.cops.peergroupmanager.PeerGroupManager;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import cn.edu.pku.ss.situationawareperceiver.ContextConstants.ContextType;

public class BusHandler extends Handler{
	
	private static final String TAG = "UIHandler";
	
	private static final String SERVICE_NAME = "cn.edu.pku.ss.situationawareperceiver";
	
	// service name constant
	private static final String GROUP_NAME = "contextsharing";
	private static final String OBJECT_PATH = "/ContextShareService";
	
    private UIHandler       mUIHandler;
    
    private ContextShareService   mContextShareService = new ContextShareService();
    private PeerGroupManager  mGroupManager;
    
	// peer state enum
	public static enum PeerConnectedState {
		DISCONNECTED, /** Not connected to the group */
		SESSION_HOLDER, /** This peer created the group */
		JOINED,	/** This peer is joined to the group */
		DISCOVERING, /** The bus attachment is discovering the group */
		SESSION_LOST, /** The client lost the group or holder quit */
	}
    private PeerConnectedState mPeerConnectedState = PeerConnectedState.DISCONNECTED;
    
    
    /* Define UIHandler codes here */
    public static final int PING = 0;
    public static final int INIT = 1;
    public static final int JOINORCREATE = 2;
    public static final int BUS_JOIN = 3;
    public static final int BUS_HANDLE_DISCONNECT = 4;
    public static final int BUS_HANDLE_PEER_CHANGED = 5;
    public static final int BUS_HANDLE_LOST_SESSION = 6;
    public static final int CLEANUP = 7;
    public static final int SEND_SHARE_REQUEST = 8;
    public static final int SEND_SHARE_MESSAGE = 9;
    
    
    /* Constructor */
    public BusHandler(Looper looper, UIHandler uiHandler, MainActivity activity) {
        super(looper);
        mUIHandler = uiHandler;
    }
    
    @Override
    public void handleMessage(Message msg) {
        switch(msg.what) {
        case PING: {
            Bundle data = msg.getData();
            String pingString = data.getString("pingString");
//            doPing(GROUP_NAME, pingString);
            break;
        }
        case INIT:{
        	doInit();
        	break;
        }
        case JOINORCREATE:{
        	doJoinOrCreate();
        	break;
        }
        case CLEANUP:{
        	if(PeerConnectedState.SESSION_HOLDER==mPeerConnectedState){
        		//destroy the group it holds
        		doDestroyGroup();
        	}
            doCleanUp();
            getLooper().quit();
        	break;
        }
        case SEND_SHARE_REQUEST:{
        	doSendRequest((ContextShareRequest)msg.obj);
        	break;
        }
        case SEND_SHARE_MESSAGE:{
        	doSendContextShareMessage((ContextShareMessage)msg.obj);
        	break;
        }
        default:
            break;
        }
    }
	private void doInit() {
		
		PeerGroupListener pgListener = new PeerGroupListener() {
        	@Override
            public void foundAdvertisedName(String groupName, short transport) {
//        		if(mPeerConnectedState == PeerConnectedState.DISCONNECTED || mPeerConnectedState == PeerConnectedState.DISCOVERING){
//        			doJoinGroup(GROUP_NAME);
//        		}
        		doJoinGroup(GROUP_NAME);
            }
            
            @Override
            public void groupLost(String groupName) {
            	if(PeerConnectedState.JOINED == mPeerConnectedState){
    				//Take over the session
    				String toastMsgStr = "Lost Session, now try to take over";
    				Message toastMsg = obtainMessage(UIHandler.TOAST_MSG, toastMsgStr);
    				sendMessage(toastMsg);
    				Log.i(TAG, toastMsgStr);
    				doJoinOrCreate();
    				return;
            	}else if(PeerConnectedState.SESSION_HOLDER == mPeerConnectedState){
    				updateGroupStatus();
    				updatePeersList();
            		return;
            	}
            	mPeerConnectedState = PeerConnectedState.SESSION_LOST;
				updateConnectStatus();
				updateGroupStatus();
				updatePeersList();

            }
            
            @Override
            public void peerAdded(String busId, String groupName, int numParticipants){
				updatePeersList();
            }
			@Override
			public void peerRemoved(String peerId, String groupName,
					int numPeers) {
				updatePeersList();
			}
        };
        if(mPeerConnectedState != PeerConnectedState.DISCONNECTED)
        	return;
        ArrayList<BusObjectData> busObjects = new ArrayList<BusObjectData>();
        busObjects.add(new BusObjectData(mContextShareService, OBJECT_PATH));
        mGroupManager = new PeerGroupManager(SERVICE_NAME, pgListener, busObjects);
        mGroupManager.registerSignalHandlers(this);
        updateGroupStatus();
        
    }
	
	/**
	 * This method is for other modules to get the peer id of the local device.
	 */
	public String getMyPeerId(){
		if(mGroupManager!=null){
			return mGroupManager.getMyPeerId();
		}else{
			return "ERROR to get my peerID: mGroupManager hasn't initialized yet.";
		}
	}
	
    private void doJoinGroup(String groupName) {
    	
    	if(PeerConnectedState.JOINED == mPeerConnectedState || PeerConnectedState.SESSION_HOLDER == mPeerConnectedState){
    		/* DISCARD! */
    		return;
    	}
        mUIHandler.logInfo("GroupManager.joinGroup(" + groupName + ")");
        
        Status status = mGroupManager.joinGroup(groupName);
        
        mUIHandler.logStatus("GroupManager.joinGroup()", status);     
        if (status == Status.OK) {
        	mPeerConnectedState = PeerConnectedState.JOINED;
        	updateConnectStatus();
        	updateGroupStatus();
        	updatePeersList();
            sendUiMessage(UIHandler.TOAST_MSG, "Joined Group: " + groupName);
        }
    }

    private void doJoinOrCreate(){
    	if(PeerConnectedState.JOINED == mPeerConnectedState || PeerConnectedState.SESSION_HOLDER == mPeerConnectedState){
    		/* DISCARD! */
    		return;
    	}
    	mUIHandler.logInfo("GroupManager.joinOrCreateGroup(" + GROUP_NAME + ")");
        JoinOrCreateReturn status = mGroupManager.joinOrCreateGroup(GROUP_NAME);
        mUIHandler.logStatus("GroupManager.createGroup()", status.getStatus());
        if (status.getStatus() == Status.OK) {           
            if (status.isJoiner()){
            	//Joined group
            	mPeerConnectedState = PeerConnectedState.JOINED;
            	sendUiMessage(UIHandler.TOAST_MSG, "Joined Group: " + GROUP_NAME);
            }
            else{
            	//Holded group
            	mPeerConnectedState = PeerConnectedState.SESSION_HOLDER;
            	sendUiMessage(UIHandler.TOAST_MSG, "Hosting Group: " + GROUP_NAME);
            }
        	updateConnectStatus();
        	updateGroupStatus();
        	updatePeersList();
        }
    }
    private void doDestroyGroup(){
    	if(PeerConnectedState.SESSION_HOLDER!=mPeerConnectedState){
    		return;
    	}
        Status status = mGroupManager.destroyGroup(GROUP_NAME);
        mUIHandler.logStatus("GroupManager.destroyGroup()", status);
    }
    private void doCleanUp(){
    	mGroupManager.cleanup();
    }
    
    private void updateConnectStatus(){
    	sendUiMessage(UIHandler.UPDATE_CONNECTION_STATUS, mPeerConnectedState.name());
    }
    private void updateGroupStatus(){
    	
    	switch(mPeerConnectedState){
    		case JOINED:{
    			sendUiMessage(UIHandler.UPDATE_GROUP_STATUS, "JOINED:"+GROUP_NAME);
    		break;
    	}
    		case SESSION_HOLDER:{
    			sendUiMessage(UIHandler.UPDATE_GROUP_STATUS, "HOLD:"+GROUP_NAME);
    			break;
    		}
    		case SESSION_LOST:{
    			sendUiMessage(UIHandler.UPDATE_GROUP_STATUS, "Lost session:"+GROUP_NAME);
    			break;
    		}
    	}
    }
    private void updatePeersList(){
    	sendUiMessage(UIHandler.UPDATE_PEERS_LIST, mGroupManager.getPeers(GROUP_NAME));
    }
    
    private void doPing(String groupName, String message) {
    	// TODO once getSignalInterface is done
    	ContextShareInterface contextShareInterface = mGroupManager.getSignalInterface(groupName, mContextShareService, ContextShareInterface.class);
        try {
            if(contextShareInterface != null) {
            	contextShareInterface.Ping(message);
            }
        } catch (BusException e) {
            e.printStackTrace();
        }
    }
    private void doSendRequest(ContextShareRequest request) {
    	ContextShareInterface contextShareInterface = mGroupManager.getSignalInterface(GROUP_NAME, mContextShareService, ContextShareInterface.class);
        try {
            if(contextShareInterface != null) {
            	
            	contextShareInterface.ReceiveShareRequest(request);
            }
        } catch (BusException e) {
            e.printStackTrace();
        }
    }
    
    private void doSendContextShareMessage(ContextShareMessage shareMsg){
    	ContextShareInterface shareInterface = mGroupManager.getSignalInterface(shareMsg.recipientPeerID, GROUP_NAME, mContextShareService, ContextShareInterface.class);
    	try{
    		if(null != shareInterface){
    			shareInterface.ReceiveContextShareMessage(shareMsg);
    		}
    	}catch(BusException e){
    		e.printStackTrace();
    	}
    }
    
    /* Helper function to send a message to the UI thread. */
    private void sendUiMessage(int what, Object obj) {
        mUIHandler.sendMessage(mUIHandler.obtainMessage(what, obj));
    }
    
    /* Service Class */
    class ContextShareService implements ContextShareInterface, BusObject {
        public void Ping(String Str) {
        }

		public void ReceiveShareRequest(ContextShareRequest request){
		
		}        
		public void ReceiveContextShareMessage(ContextShareMessage shareMsg){
			
		}
    }
    /* Implement the signal method */
    @BusSignalHandler(iface = "cn.edu.pku.ss.situationperceiver.ContextShareInterface", signal = "Ping")
    public void Ping(String str) {
    	// TODO once getSenderBusId() is done
        sendUiMessage(UIHandler.TOAST_MSG, "User" + mGroupManager.getSenderPeerId() + "  Message: " + str);
    }
    @BusSignalHandler(iface = "cn.edu.pku.ss.situationperceiver.ContextShareInterface", signal = "ReceiveShareRequest")
    public void ReceiveShareRequest(ContextShareRequest request) {
    	// TODO once getSenderBusId() is done
    	ContextType[] typeList = ContextType.values();
    	String msgStr = ". Request Context type: " + typeList[request.contextTypeInt].name() + " date: " + request.requestTime;
        sendUiMessage(UIHandler.TOAST_MSG, " User:" + mGroupManager.getSenderPeerId() + msgStr);
        //Call ContextShareManager to handle it
        ContextShareManager.getInstance().receiveContextShareRequest(request);
    }
    @BusSignalHandler(iface = "cn.edu.pku.ss.situationperceiver.ContextShareInterface", signal = "ReceiveContextShareMessage")
    public void ReceiveContextShareMessage(ContextShareMessage shareMsg){
    	ContextType[] typeList = ContextType.values();
    	String msgStr = " Received a shared context from" + shareMsg.senderPeerId +  ". Context: " + typeList[shareMsg.contextTypeInt].name() + " truthvalue: " + shareMsg.truthValue;
        sendUiMessage(UIHandler.TOAST_MSG, msgStr);
        //Call ContextShareManager to Handle this~
        ContextShareManager.getInstance().handleSharedContext(shareMsg);
	}
}
